﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Diagnostics;
using System.Collections.Generic;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Imaging;
using WP7Contrib.View.Controls.Extensions;

namespace MetroInMotionUtils
{
  public static class MetroInMotion
  {



    /// <summary>
    /// Animates each element in order, creating a 'peel' effect. The supplied action
    /// is invoked when the animation ends.
    /// </summary>
    public static void Peel(this IEnumerable<FrameworkElement> elements, Action endAction)
    {
      var elementList = elements.ToList();
      var lastElement = elementList.Last();

      // iterate over all the elements, animating each of them
      double delay = 0;
      foreach (FrameworkElement element in elementList)
      {
        var sb = GetPeelAnimation(element, delay);

        // add a Completed event handler to the last element
        if (element.Equals(lastElement))
        {
          sb.Completed += (s, e) =>
            {
              endAction();
            };
        }

        sb.Begin();
        delay += 50;
      }
    }

    /// <summary>
    /// Creates a PlaneProjection and associates it with the given element, returning
    /// a Storyboard which will animate the PlaneProjection to 'peel' the item
    /// from the screen.
    /// </summary>
    private static Storyboard GetPeelAnimation(FrameworkElement element, double delay)
    {
      Storyboard sb;

      var projection = new PlaneProjection()
      {
        CenterOfRotationX = -0.1
      };
      element.Projection = projection;

      // compute the angle of rotation required to make this element appear
      // at a 90 degree angle at the edge of the screen.
      var width = element.ActualWidth;
      var targetAngle = Math.Atan(1000 / (width / 2));
      targetAngle = targetAngle * 180 / Math.PI;

      // animate the projection
      sb = new Storyboard();
      sb.BeginTime = TimeSpan.FromMilliseconds(delay);      
      sb.Children.Add(CreateAnimation(0, -(180 - targetAngle), 0.3, "RotationY", projection));
      sb.Children.Add(CreateAnimation(0, 23, 0.3, "RotationZ", projection));
      sb.Children.Add(CreateAnimation(0, -23, 0.3, "GlobalOffsetZ", projection));      
      return sb;
    }

    private static DoubleAnimation CreateAnimation(double? from, double? to, double duration,
      string targetProperty, DependencyObject target)
    {
      var db = new DoubleAnimation();
      db.To = to;
      db.From = from;
      db.EasingFunction = new SineEase();
      db.Duration = TimeSpan.FromSeconds(duration);
      Storyboard.SetTarget(db, target);
      Storyboard.SetTargetProperty(db, new PropertyPath(targetProperty));
      return db;
    }

   

  }

  /// <summary>
  /// Animates an element so that it flies out and flies in!
  /// </summary>
  public class ItemFlyInAndOutAnimations
  {
    private Popup _popup;

    private Canvas _popupCanvas;

    private FrameworkElement _targetElement;

    private Point _targetElementPosition;

    private Image _targetElementClone;

    private PhoneApplicationPage _page;

    private static TimeSpan _flyInSpeed = TimeSpan.FromMilliseconds(300);

    private static TimeSpan _flyOutSpeed = TimeSpan.FromMilliseconds(300);

    public ItemFlyInAndOutAnimations()
    {
      // construct a popup, with a Canvas as its child
      _popup = new Popup();
      _popupCanvas = new Canvas();
      _popup.Child = _popupCanvas;
    }

    /// <summary>
    /// Animate the previously 'flown-out' element back to its original location.
    /// </summary>
    public void ItemFlyIn()
    {
      if (_popupCanvas.Children.Count != 1)
        return;

      _popup.IsOpen = true;
      _page.Opacity = 1.0;

      Image animatedImage = _popupCanvas.Children[0] as Image;

      var sb = new Storyboard();

      // animate the X position
      var db = CreateDoubleAnimation(_targetElementPosition.X - 100, _targetElementPosition.X,
          new SineEase(),
          _targetElementClone, Canvas.LeftProperty, _flyInSpeed);
      sb.Children.Add(db);

      // animate the Y position
      db = CreateDoubleAnimation(_targetElementPosition.Y - 50, _targetElementPosition.Y,
          new SineEase(),
          _targetElementClone, Canvas.TopProperty, _flyInSpeed);
      sb.Children.Add(db);

      sb.Completed += (s, e) =>
        {
          // when the animation has finished, hide the popup once more
          _popup.IsOpen = false;

          // restore the element we have animated
          _targetElement.Opacity = 1.0;

          // and get rid of our clone
          _popupCanvas.Children.Clear();
        };

      sb.Begin();
    }


    /// <summary>
    /// Animate the given element so that it flies off screen, fading 
    /// everything else that is on screen.
    /// </summary>
    public void ItemFlyOut(PhoneApplicationPage page, FrameworkElement element, Action action)
    {
      _targetElement = element;
      _page = page;
      var rootElement = Application.Current.RootVisual as FrameworkElement;


      _targetElementClone = new Image()
      {
        Source = new WriteableBitmap(element, null)
      };
      _popupCanvas.Children.Add(_targetElementClone);

      _targetElementPosition = element.GetRelativePosition(rootElement);
      Canvas.SetTop(_targetElementClone, _targetElementPosition.Y);
      Canvas.SetLeft(_targetElementClone, _targetElementPosition.X);

      var sb = new Storyboard();

      // animate the X position
      var db = CreateDoubleAnimation(_targetElementPosition.X, _targetElementPosition.X + 500,
          new SineEase() { EasingMode = EasingMode.EaseIn },
          _targetElementClone, Canvas.LeftProperty, _flyOutSpeed);
      sb.Children.Add(db);

      // animate the Y position
      db = CreateDoubleAnimation(_targetElementPosition.Y, _targetElementPosition.Y + 50,
          new SineEase() { EasingMode = EasingMode.EaseOut },
          _targetElementClone, Canvas.TopProperty, _flyOutSpeed);
      sb.Children.Add(db);     
      
      // fade out the other elements
      db = CreateDoubleAnimation(1, 0,
          null, page, UIElement.OpacityProperty, _flyOutSpeed);
      sb.Children.Add(db);

      sb.Completed += (s, e2) =>
        {
          action();

          // hide the popup, by placing a task on the dispatcher queue, this
          // should be executed after the navigation has occurred
          element.Dispatcher.BeginInvoke(() =>
          {
            _popup.IsOpen = false;
          });
        };

      // hide the element we have 'cloned' into the popup
      element.Opacity = 0.0;

      // open the popup
      _popup.IsOpen = true;

      // begin the animation
      sb.Begin();
    }

    public static DoubleAnimation CreateDoubleAnimation(double from, double to, IEasingFunction easing,
      DependencyObject target, object propertyPath, TimeSpan duration)
    {
      var db = new DoubleAnimation();
      db.To = to;
      db.From = from;
      db.EasingFunction = easing;
      db.Duration = duration;
      Storyboard.SetTarget(db, target);
      Storyboard.SetTargetProperty(db, new PropertyPath(propertyPath));
      return db;
    }
  }
}
